const puppeteer = require('puppeteer');
const _ = require('lodash');
const queue = require('async/queue');
const path = require('path');
const fs = require('fs');
const http = require('http');
const ms = require('ms');
const defaultOptions = {
    parallel: 1,
    /*launch: {
        headless: false,
        executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
        args: []
    },*/
    // connect: 'url'
};
let browser, q;

/**
 * Chrome
 */
class Chrome {
    constructor(options) {
        this.__init = false;
        this.__options = _.merge({}, defaultOptions, options);
        this.__successLength = 0;
        this.__failLength = 0;
    }

    /**
     * 启动Chrome
     * @returns {Promise<Chrome>}
     */
    async launch() {
        //不存在存在配置，启动一次Chrome生成配置文件
        if (!this.___hasPreferences()) {
            console.log('不存在配置文件, 先启动一次Chrome，关闭后再初始化设置');
            browser = await this.___launch();
            await browser.close();
        }
        this.___modifyPreferences();

        //正式启动Chrome
        browser = await this.___launch();

        this.reset();
        this.__init = true;
        browser.on('disconnected', () => {
            console.log('Chrome 崩溃了，正在重启Chrome');
            setTimeout(async () => {
                await this.launch();
            }, 500);
        });
        return this;
    };

    /**
     * 检查配置
     */
    ___hasPreferences() {
        let _path = path.join(this.__options.userPath, 'Default', 'Preferences');
        let exists = fs.existsSync(_path);
        console.log('Chrome 配置文件 存在', exists);
        return exists;
    }

    /**
     * 修改Chrome配置
     * @private
     */
    ___modifyPreferences() {
        if (!this.___hasPreferences())
            return;
        console.log('开始注入 Chrome 设置');
        let _path = path.join(this.__options.userPath, 'Default', 'Preferences');
        let preferences = JSON.parse(fs.readFileSync(_path));
        let plugins = {};
        let now = Date.now();
        let values = [
            'egame.qq.com',
            // 'bilibili.com', //已经支持html5播放器
            'chushou.tv',
            // 'douyu.com', //已经支持html5播放器
            'huajiao.com',
            'huomao.com',
            'huya.com',
            'longzhu.com',
            // 'panda.tv', //已经支持html5播放器
            'quanmin.tv',
            'zhanqi.com',
            'zhanqi.tv',
            'live.ent.163.com',
        ];
        values.forEach(v => {
            let key = '[*.]' + v + ',*';
            plugins[key] = {
                "last_modified": now,
                "setting": 1
            };
        });
        preferences.profile.content_settings.exceptions.plugins = plugins;
        fs.writeFileSync(_path, JSON.stringify(preferences));
    }

    async ___launch() {
        let browser;
        if (!!this.__options.launch)
            browser = await
                puppeteer.launch(this.__options.launch);
        else if (!!this.__options.connect) {
            const info = {browserWSEndpoint: await getChromeWSUrl(this.__options.connect)};
            // console.log(info);
            browser = await puppeteer.connect(info);
        }
        if (!browser)
            throw new Error('没有与Chrome浏览器连接');
        return browser;
    }

    /**
     * 添加截图任务，返回网页图片
     * @param task <module.Task>
     * @returns {Promise<any>}
     */
    async addTask(task) {
        return new Promise(resolve => {
            this.__q.push(task, result => {
                resolve(result);
            });
        });
    }

    /**
     * 关闭Chrome
     * @returns {Promise<void>}
     */
    async close() {
        await browser.close();
        browser = undefined;
    }

    /**
     * 状态统计
     * @returns {{wait: number, success: number, fail: number}}
     */
    get state() {
        let wait = this.__q ? this.__q.length() : 0;
        return {wait, success: this.__successLength, fail: this.__failLength};
    }

    get isReady() {
        return this.__init;
    }

    /**
     * 初始化 或 重置
     */
    reset() {
        this.__q = queue((task, callback) => {
            (async () => {
                let page = await browser.newPage();

                let result = null, start = Date.now();
                try {
                    await task.init(page);
                    result = {};
                    result.buffer = await page.screenshot({type: 'jpeg', quality: 100});
                    console.log('任务耗时', ms(Date.now() - start), task.__url);
                    this.__successLength++;
                } catch (e) {
                    console.log('\x1b[31m%s\x1b[0m', '网页错误 ' + task.__url);
                    console.log(e);
                    this.__failLength++;
                }
                await page.close();
                callback(result);
            })();
        }, this.__options.parallel);
    }
}

module.exports = Chrome;

/**
 * 获取与Chrome DevTool连接信息
 * @param url
 * @returns {Promise<any>}
 */
function getChromeWSUrl(url) {
    return new Promise((resolve, reject) => {
        http.get(url, stream => {
            const {statusCode} = stream;
            if (statusCode !== 200)
                return reject(new Error('请求失败，没有启动Chrome吗？'));

            let data = '';
            stream.on('data', (chunk) => {
                data += chunk;
            });
            stream.on('end', () => {
                console.log(data);
                resolve(JSON.parse(data).webSocketDebuggerUrl);
            });
        });
        setTimeout(() => {
            reject(new Error('与Chrome连接超时，没有启动Chrome吧？'))
        }, 100);
    });
}